{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "79a3f6a4-8348-4cd6-a336-10ecb9cfaaa3",
   "metadata": {},
   "source": [
    "# 对话代理 Conversational agent\n",
    "\n",
    " - [一、设置OpenAI API Key](#一、设置OpenAI-API-Key)\n",
    " - [二、逐步构建Agent](#二、逐步构建Agent)\n",
    "     - [2.1 定义Tools和Function](##2.1-定义Tools和Function)\n",
    "     - [2.2 加入中间步骤结果](##2.2-加入中间步骤结果)\n",
    "     - [2.3 加入对话历史](##2.3-加入对话历史)\n",
    " - [三、创建对话机器人](#三、创建对话机器人)\n",
    " - [四、英文版提示](#四、英文版提示)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "5871cf03",
   "metadata": {},
   "source": [
    "# 一、设置OpenAI-API-Key\n",
    "\n",
    "详细内容见`设置OpenAI_API_KEY.ipynb`文件"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "985f0c67",
   "metadata": {},
   "source": [
    "# 二、逐步构建Agent"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "9131b422",
   "metadata": {},
   "source": [
    "## 2.1 定义Tools和Function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "20f44fbb-6708-4fce-bac4-55dc261e3a2d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入tool包\n",
    "from langchain.tools import tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "aa7406eb-334b-43d0-8110-31656a55b2fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入所需的库\n",
    "import requests\n",
    "from pydantic import BaseModel, Field\n",
    "import datetime\n",
    "\n",
    "# 定义输入格式\n",
    "class OpenMeteoInput(BaseModel):\n",
    "    latitude: float = Field(..., description=\"要获取天气数据的位置的纬度\") \n",
    "    longitude: float = Field(..., description=\"要获取天气数据的位置的经度\") \n",
    "\n",
    "# 使用 @tool 装饰器并指定输入格式\n",
    "@tool(args_schema=OpenMeteoInput)\n",
    "def get_current_temperature(latitude: float, longitude: float) -> dict:\n",
    "    \"\"\"\"根据给定的坐标位置获得温度\"\"\"\n",
    "    \n",
    "    # Open Meteo API 的URL\n",
    "    BASE_URL = \"https://api.open-meteo.com/v1/forecast\"\n",
    "    \n",
    "    # 请求参数\n",
    "    params = {\n",
    "        'latitude': latitude,\n",
    "        'longitude': longitude,\n",
    "        'hourly': 'temperature_2m',\n",
    "        'forecast_days': 1,\n",
    "    }\n",
    "\n",
    "    # 发送 API 请求\n",
    "    response = requests.get(BASE_URL, params=params)\n",
    "    \n",
    "    # 检查响应状态码\n",
    "    if response.status_code == 200:\n",
    "        # 解析 JSON 响应\n",
    "        results = response.json()\n",
    "    else:\n",
    "        # 处理请求失败的情况\n",
    "        raise Exception(f\"API Request failed with status code: {response.status_code}\")\n",
    "\n",
    "    # 获取当前 UTC 时间\n",
    "    current_utc_time = datetime.datetime.utcnow()\n",
    "    \n",
    "    # 将时间字符串转换为 datetime 对象\n",
    "    time_list = [datetime.datetime.fromisoformat(time_str.replace('Z', '+00:00')) for time_str in results['hourly']['time']]\n",
    "    \n",
    "    # 获取温度列表\n",
    "    temperature_list = results['hourly']['temperature_2m']\n",
    "    \n",
    "    # 找到最接近当前时间的索引\n",
    "    closest_time_index = min(range(len(time_list)), key=lambda i: abs(time_list[i] - current_utc_time))\n",
    "    \n",
    "    # 获取当前温度\n",
    "    current_temperature = temperature_list[closest_time_index]\n",
    "    \n",
    "    # 返回当前温度的字符串形式\n",
    "    return f'现在温度是 {current_temperature}°C'\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "3ac0a1e7-567f-4775-a538-351a5ff099e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "import wikipedia\n",
    "\n",
    "# 定义维基百科搜索的tool\n",
    "@tool\n",
    "def search_wikipedia(query: str) -> str:\n",
    "    \"\"\"Run Wikipedia search and get page summaries.\"\"\"\n",
    "    page_titles = wikipedia.search(query)\n",
    "    summaries = []\n",
    "    for page_title in page_titles[: 3]: #取前三个页面标题\n",
    "        try:\n",
    "            #使用 wikipedia 模块的 page 函数，获取指定标题的维基百科页面对象。\n",
    "            wiki_page =  wikipedia.page(title=page_title, auto_suggest=False) \n",
    "            # 获取页面摘要\n",
    "            summaries.append(f\"页面: {page_title}\\n摘要: {wiki_page.summary}\")\n",
    "        except (\n",
    "            self.wiki_client.exceptions.PageError,\n",
    "            self.wiki_client.exceptions.DisambiguationError,\n",
    "        ):\n",
    "            pass\n",
    "    if not summaries:\n",
    "        return \"维基百科没有搜索到合适的结果\"\n",
    "    return \"\\n\\n\".join(summaries)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "4856d233-0916-4b97-8a7c-b494d5f2b8e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 加入tools列表\n",
    "tools = [get_current_temperature, search_wikipedia]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "cdf0d789-559b-42df-9d6d-77a0cd8888d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain.tools.render import format_tool_to_openai_function\n",
    "from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "933ce7cb-099c-4f43-a548-b355a5b66511",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将工具格式化为 OpenAI 函数\n",
    "functions = [format_tool_to_openai_function(f) for f in tools]\n",
    "\n",
    "# 创建 ChatOpenAI 模型，设置温度为 0，绑定生成的 OpenAI 函数列表\n",
    "model = ChatOpenAI(temperature=0).bind(functions=functions)\n",
    "\n",
    "# 创建 ChatPromptTemplate，从消息模板中获取用户输入\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"You are helpful but sassy assistant\"),\n",
    "    (\"user\", \"{input}\"),\n",
    "])\n",
    "\n",
    "# 创建处理链，将 prompt、model 和 OpenAIFunctionsAgentOutputParser 连接起来\n",
    "chain = prompt | model | OpenAIFunctionsAgentOutputParser()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "4281a2d8-7884-40e0-b34d-09c39eecbb7a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用\n",
    "result = chain.invoke({\"input\": \"现在圣佛朗西斯科的温度是多少?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "b1dbc697-3e0a-4197-8ddc-2894763b4b85",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'get_current_temperature'"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看调用的工具\n",
    "result.tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "ab829b59-803f-488f-bcf3-5119c224baa0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'latitude': 37.7749, 'longitude': -122.4194}"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看工具的输入\n",
    "result.tool_input"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "bc7b379b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'get_current_temperature'"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result.tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "cd378dff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'latitude': 37.7749, 'longitude': -122.4194}"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result.tool_input"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4a8e21dd",
   "metadata": {},
   "source": [
    "## 2.2 加入中间步骤结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "52b4d483-c498-4932-a373-bc2f4da17c7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建 ChatPromptTemplate，从消息模板中获取用户输入\n",
    "from langchain.prompts import MessagesPlaceholder\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个安全且乐于助人的助手\"),\n",
    "    (\"user\", \"{input}\"),\n",
    "    MessagesPlaceholder(variable_name=\"agent_scratchpad\") # 解析和验证关键字参数中的输入数据\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "66ac6ea2-0e20-424a-ba33-6f8d627379b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建处理链，将 prompt、model 和 OpenAIFunctionsAgentOutputParser 连接起来\n",
    "chain = prompt | model | OpenAIFunctionsAgentOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "bada1de8-d7c4-4880-98c2-d47cc65f684d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用\n",
    "result1 = chain.invoke({\n",
    "    \"input\": \"现在圣佛朗西斯科的温度是多少?\",\n",
    "    \"agent_scratchpad\": []\n",
    "})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "8d5f4e61-36a3-4d80-9c49-0b83cedb1592",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'get_current_temperature'"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看调用的工具\n",
    "result1.tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "11845c11-7d41-455a-8ac0-4b8161acaf83",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取tool调用的结果\n",
    "observation = get_current_temperature(result1.tool_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "810184c8-8495-4189-b83e-17220cf65bbe",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'现在温度是 9.2°C'"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看结果\n",
    "observation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "8c1b223d-ea2a-414c-924c-512c4c9da66a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain_core.agents.AgentActionMessageLog"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看输出类型\n",
    "type(result1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "61853679-75b5-4462-8059-f1214286a9eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents.format_scratchpad import format_to_openai_functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "72ca4278-165a-4915-8ec6-09e696f29d86",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"latitude\":37.7749,\"longitude\":-122.4194}', 'name': 'get_current_temperature'}})]"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看调用日志\n",
    "result1.message_log"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "27e079a7-d58c-41c0-8762-4e183e72a1d1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"latitude\":37.7749,\"longitude\":-122.4194}', 'name': 'get_current_temperature'}}),\n",
       " FunctionMessage(content='现在温度是 9.2°C', name='get_current_temperature')]"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将Function和Tool的结果转换成openai函数格式\n",
    "format_to_openai_functions([(result1, observation), ])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "09735a9e-452d-472b-a4c0-35c315e9e53f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 构造成Agent的输入格式，agent_scratchpad是中间步骤的结果\n",
    "result2 = chain.invoke({\n",
    "    \"input\": \"现在圣佛朗西斯科的温度是多少?\", \n",
    "    \"agent_scratchpad\": format_to_openai_functions([(result1, observation)])\n",
    "})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "9df61ecc-52fe-4fae-85a2-dbb61d68e2db",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AgentFinish(return_values={'output': '圣佛朗西斯科现在的温度是9.2°C。'}, log='圣佛朗西斯科现在的温度是9.2°C。')"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看Agent的输出结果\n",
    "result2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "d9dd139f-e06a-4148-a025-ae100851c63e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.schema.agent import AgentFinish\n",
    "\n",
    "def run_agent(user_input):\n",
    "    # 存储中间步骤的列表\n",
    "    intermediate_steps = []\n",
    "\n",
    "    # 循环执行 agent 操作\n",
    "    while True:\n",
    "        # 调用处理链的 invoke 方法，传递用户输入和中间步骤的 OpenAI 函数形式\n",
    "        result = chain.invoke({\n",
    "            \"input\": user_input,\n",
    "            \"agent_scratchpad\": format_to_openai_functions(intermediate_steps)\n",
    "        })\n",
    "\n",
    "        # 如果结果是 AgentFinish 类型，则结束运行并返回结果\n",
    "        if isinstance(result, AgentFinish):\n",
    "            return result\n",
    "\n",
    "        # 根据结果中的工具名称选择相应的工具函数\n",
    "        tool = {\n",
    "            \"search_wikipedia\": search_wikipedia,\n",
    "            \"get_current_temperature\": get_current_temperature,\n",
    "        }[result.tool]\n",
    "\n",
    "        # 运行选定的工具函数，并获取观察结果\n",
    "        observation = tool.run(result.tool_input)\n",
    "\n",
    "        # 将结果和观察结果添加到中间步骤列表\n",
    "        intermediate_steps.append((result, observation))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "4f6c1121-70a6-41ae-8c3d-41f7cfc756af",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.schema.runnable import RunnablePassthrough\n",
    "\n",
    "# 创建 RunnablePassthrough，用于将 agent_scratchpad存储的中间步骤结果转换为 OpenAI 函数形式\n",
    "agent_chain = RunnablePassthrough.assign(\n",
    "    agent_scratchpad=lambda x: format_to_openai_functions(x[\"intermediate_steps\"])\n",
    ") | chain\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "4a05df88-4ad9-4c48-a7dd-6b42db91cb33",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义一个运行 agent 的函数\n",
    "def run_agent(user_input):\n",
    "    # 存储中间步骤的列表\n",
    "    intermediate_steps = []\n",
    "\n",
    "    # 循环执行 agent 操作\n",
    "    while True:\n",
    "        # 通过 agent_chain 调用 invoke 方法，传递用户输入和中间步骤的列表\n",
    "        result = agent_chain.invoke({\n",
    "            \"input\": user_input,\n",
    "            \"intermediate_steps\": intermediate_steps\n",
    "        })\n",
    "\n",
    "        # 如果结果是 AgentFinish 类型，则结束循环并返回结果\n",
    "        if isinstance(result, AgentFinish):\n",
    "            return result\n",
    "\n",
    "        # 根据结果中的工具名称选择相应的工具函数\n",
    "        tool = {\n",
    "            \"search_wikipedia\": search_wikipedia,\n",
    "            \"get_current_temperature\": get_current_temperature,\n",
    "        }[result.tool]\n",
    "\n",
    "        # 运行选定的工具函数，并获取观察结果\n",
    "        observation = tool.run(result.tool_input)\n",
    "\n",
    "        # 将结果和观察结果添加到中间步骤列表\n",
    "        intermediate_steps.append((result, observation))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "23417203",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AgentFinish(return_values={'output': '圣弗朗西斯科现在的温度是9.2°C。'}, log='圣弗朗西斯科现在的温度是9.2°C。')"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run_agent(\"圣弗朗西斯科的温度是多少？\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "d96a0838",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AgentFinish(return_values={'output': 'LangChain是一个旨在简化使用大型语言模型（LLMs）创建应用程序的框架。作为一个语言模型集成框架，LangChain的用例主要涵盖了文档分析和摘要、聊天机器人以及代码分析等领域。'}, log='LangChain是一个旨在简化使用大型语言模型（LLMs）创建应用程序的框架。作为一个语言模型集成框架，LangChain的用例主要涵盖了文档分析和摘要、聊天机器人以及代码分析等领域。')"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run_agent(\"什么是langchain?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "267d33eb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AgentFinish(return_values={'output': '你好！有什么可以帮助你的吗？'}, log='你好！有什么可以帮助你的吗？')"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run_agent(\"你好！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "b270a064-8cf1-44da-a59e-be622f9099b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用AgentExecutor对agent进行封装\n",
    "from langchain.agents import AgentExecutor\n",
    "agent_executor = AgentExecutor(agent=agent_chain, tools=tools, verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "e52292b2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `search_wikipedia` with `{'query': 'Langchain'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[33;1m\u001b[1;3mPage: LangChain\n",
      "Summary: LangChain is a framework designed to simplify the creation of applications using large language models (LLMs). As a language model integration framework, LangChain's use-cases largely overlap with those of language models in general, including document analysis and summarization, chatbots, and code analysis.\n",
      "\n",
      "Page: OpenAI\n",
      "Summary: OpenAI is a U.S. based artificial intelligence (AI) research organization founded in December 2015, researching artificial intelligence with the goal of developing \"safe and beneficial\" artificial general intelligence, which it defines as \"highly autonomous systems that outperform humans at most economically valuable work\".\n",
      "As one of the leading organizations of the AI spring, it has developed several large language models, advanced image generation models, and previously, released open-source models. Its release of ChatGPT has been credited with starting the AI spring.The organization consists of the non-profit OpenAI, Inc. registered in Delaware and its for-profit subsidiary OpenAI Global, LLC. It was founded by Ilya Sutskever, Greg Brockman, Trevor Blackwell, Vicki Cheung, Andrej Karpathy, Durk Kingma, Jessica Livingston, John Schulman, Pamela Vagata, and Wojciech Zaremba, with Sam Altman and Elon Musk serving as the initial board members. Microsoft provided OpenAI Global LLC with a $1 billion investment in 2019 and a $10 billion investment in 2023, with a significant portion of the investment in the form of computational resources on Microsoft's Azure cloud service.On November 17, 2023, the board removed Altman as CEO, while Brockman was removed as chairman and then resigned as president. Four days later, both returned after negotiations with the board, and most of the board members resigned. The new initial board included former Salesforce co-CEO Bret Taylor as chairman. It was also announced that Microsoft will have a non-voting board seat.\n",
      "\n",
      "Page: DataStax\n",
      "Summary: DataStax, Inc. is a real-time data for AI company based in Santa Clara, California. Its product Astra DB is a cloud database-as-a-service based on Apache Cassandra. DataStax also offers DataStax Enterprise (DSE), an on-premises database built on Apache Cassandra, and Astra Streaming, a messaging and event streaming cloud service based on Apache Pulsar. As of June 2022, the company has roughly 800 customers distributed in over 50 countries.\u001b[0m\u001b[32;1m\u001b[1;3mLangChain 是一个旨在简化使用大型语言模型（LLMs）创建应用程序的框架。作为一个语言模型集成框架，LangChain 的用例主要涵盖了文档分析和摘要、聊天机器人以及代码分析等领域。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '什么是langchain?',\n",
       " 'output': 'LangChain 是一个旨在简化使用大型语言模型（LLMs）创建应用程序的框架。作为一个语言模型集成框架，LangChain 的用例主要涵盖了文档分析和摘要、聊天机器人以及代码分析等领域。'}"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"什么是langchain?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "5ca7b010",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m你好，Bob！有什么可以帮助你的吗？\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '我的名字是bob', 'output': '你好，Bob！有什么可以帮助你的吗？'}"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"我的名字是bob\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "ee259c12",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m你的名字是用户。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '我的名字是什么？', 'output': '你的名字是用户。'}"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"我的名字是什么？\"})"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4d180426",
   "metadata": {},
   "source": [
    "## 3.3 加入对话历史"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "7d1df28a-27da-4f8e-ab03-a37a276db74f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 加入用户的对话历史\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"You are helpful but sassy assistant\"),\n",
    "    MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "    (\"user\", \"{input}\"),\n",
    "    MessagesPlaceholder(variable_name=\"agent_scratchpad\")\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "a8598e87-de8a-4710-a90e-2a919adc00e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 构造agent的处理链\n",
    "agent_chain = RunnablePassthrough.assign(\n",
    "    agent_scratchpad= lambda x: format_to_openai_functions(x[\"intermediate_steps\"])\n",
    ") | prompt | model | OpenAIFunctionsAgentOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "a3865c2c-cb80-4341-bcbe-3b5150749622",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 加入对话记忆模块\n",
    "from langchain.memory import ConversationBufferMemory\n",
    "memory = ConversationBufferMemory(return_messages=True,memory_key=\"chat_history\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "40b036fc-446f-4075-9a92-9a4bac6ecb2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 封装AgentExecutor\n",
    "agent_executor = AgentExecutor(agent=agent_chain, tools=tools, verbose=True, memory=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "bad2f24b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mNice to meet you, Bob! How can I assist you today?\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '我的名字是bob',\n",
       " 'chat_history': [HumanMessage(content='my name is bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?'),\n",
       "  HumanMessage(content='我的名字是bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?')],\n",
       " 'output': 'Nice to meet you, Bob! How can I assist you today?'}"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"我的名字是bob\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "c7c51371",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m你的名字是 Bob。有什么我可以帮助你的吗？\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '我的名字是什么？',\n",
       " 'chat_history': [HumanMessage(content='my name is bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?'),\n",
       "  HumanMessage(content='我的名字是bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?'),\n",
       "  HumanMessage(content='whats my name'),\n",
       "  AIMessage(content='Your name is Bob. How can I assist you today, Bob?'),\n",
       "  HumanMessage(content='我的名字是什么？'),\n",
       "  AIMessage(content='你的名字是 Bob。有什么我可以帮助你的吗？')],\n",
       " 'output': '你的名字是 Bob。有什么我可以帮助你的吗？'}"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"我的名字是什么？\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "efadf455",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `get_current_temperature` with `{'latitude': 37.7749, 'longitude': -122.4194}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3mThe current temperature is 9.1°C\u001b[0m\u001b[32;1m\u001b[1;3mThe current temperature in San Francisco is 9.1°C. Is there anything else you would like to know?\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '圣弗朗西斯科现在的温度是多少？',\n",
       " 'chat_history': [HumanMessage(content='my name is bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?'),\n",
       "  HumanMessage(content='我的名字是bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?'),\n",
       "  HumanMessage(content='whats my name'),\n",
       "  AIMessage(content='Your name is Bob. How can I assist you today, Bob?'),\n",
       "  HumanMessage(content='我的名字是什么？'),\n",
       "  AIMessage(content='你的名字是 Bob。有什么我可以帮助你的吗？'),\n",
       "  HumanMessage(content='whats the weather in sf?'),\n",
       "  AIMessage(content='The current temperature in San Francisco is 9.1°C. Is there anything else you would like to know?'),\n",
       "  HumanMessage(content='圣弗朗西斯科现在的温度是多少？'),\n",
       "  AIMessage(content='The current temperature in San Francisco is 9.1°C. Is there anything else you would like to know?')],\n",
       " 'output': 'The current temperature in San Francisco is 9.1°C. Is there anything else you would like to know?'}"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"圣弗朗西斯科现在的温度是多少？\"})"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "34b0b511-21d6-4c7e-b4e2-f886bc296997",
   "metadata": {},
   "source": [
    "# 三、 创建对话机器人"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "b1cd4bd2-703e-4e4e-8f5c-ae3d491a41f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 自定义的工具（自由发挥）\n",
    "@tool\n",
    "def create_your_own(query: str) -> str:\n",
    "    \"\"\"可以自定义的功能函数 \"\"\"\n",
    "    print(type(query))\n",
    "    return query[::-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "67cb6fdc-ff55-4a7b-8a39-1871006aa990",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将之前定义的工具加入工具列表\n",
    "tools = [get_current_temperature, search_wikipedia, create_your_own]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "66a73e69-acfb-471e-b54d-8ac586235521",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": "(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  var force = true;\n  var py_version = '3.3.4'.replace('rc', '-rc.').replace('.dev', '-dev.');\n  var reloading = false;\n  var Bokeh = root.Bokeh;\n\n  if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) {\n        if (callback != null)\n          callback();\n      });\n    } finally {\n      delete root._bokeh_onload_callbacks;\n    }\n    console.debug(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n    if (css_urls == null) css_urls = [];\n    if (js_urls == null) js_urls = [];\n    if (js_modules == null) js_modules = [];\n    if (js_exports == null) js_exports = {};\n\n    root._bokeh_onload_callbacks.push(callback);\n\n    if (root._bokeh_is_loading > 0) {\n      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    }\n    if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n      run_callbacks();\n      return null;\n    }\n    if (!reloading) {\n      console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n    }\n\n    function on_load() {\n      root._bokeh_is_loading--;\n      if (root._bokeh_is_loading === 0) {\n        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n        run_callbacks()\n      }\n    }\n    window._bokeh_on_load = on_load\n\n    function on_error() {\n      console.error(\"failed to load \" + url);\n    }\n\n    var skip = [];\n    if (window.requirejs) {\n      window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n      require([\"jspanel\"], function(jsPanel) {\n\twindow.jsPanel = jsPanel\n\ton_load()\n      })\n      require([\"jspanel-modal\"], function() {\n\ton_load()\n      })\n      require([\"jspanel-tooltip\"], function() {\n\ton_load()\n      })\n      require([\"jspanel-hint\"], function() {\n\ton_load()\n      })\n      require([\"jspanel-layout\"], function() {\n\ton_load()\n      })\n      require([\"jspanel-contextmenu\"], function() {\n\ton_load()\n      })\n      require([\"jspanel-dock\"], function() {\n\ton_load()\n      })\n      require([\"gridstack\"], function(GridStack) {\n\twindow.GridStack = GridStack\n\ton_load()\n      })\n      require([\"notyf\"], function() {\n\ton_load()\n      })\n      root._bokeh_is_loading = css_urls.length + 9;\n    } else {\n      root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n    }\n\n    var existing_stylesheets = []\n    var links = document.getElementsByTagName('link')\n    for (var i = 0; i < links.length; i++) {\n      var link = links[i]\n      if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n      }\n    }\n    for (var i = 0; i < css_urls.length; i++) {\n      var url = css_urls[i];\n      if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n      }\n      const element = document.createElement(\"link\");\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.rel = \"stylesheet\";\n      element.type = \"text/css\";\n      element.href = url;\n      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n      document.body.appendChild(element);\n    }    if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n      var urls = ['https://cdn.holoviz.org/panel/1.3.8/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.3.8/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.3.8/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.3.8/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.3.8/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.3.8/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.3.8/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n      for (var i = 0; i < urls.length; i++) {\n        skip.push(urls[i])\n      }\n    }    if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n      var urls = ['https://cdn.holoviz.org/panel/1.3.8/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n      for (var i = 0; i < urls.length; i++) {\n        skip.push(urls[i])\n      }\n    }    if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n      var urls = ['https://cdn.holoviz.org/panel/1.3.8/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n      for (var i = 0; i < urls.length; i++) {\n        skip.push(urls[i])\n      }\n    }    var existing_scripts = []\n    var scripts = document.getElementsByTagName('script')\n    for (var i = 0; i < scripts.length; i++) {\n      var script = scripts[i]\n      if (script.src != null) {\n\texisting_scripts.push(script.src)\n      }\n    }\n    for (var i = 0; i < js_urls.length; i++) {\n      var url = js_urls[i];\n      if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t  on_load();\n\t}\n\tcontinue;\n      }\n      var element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n    for (var i = 0; i < js_modules.length; i++) {\n      var url = js_modules[i];\n      if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t  on_load();\n\t}\n\tcontinue;\n      }\n      var element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      element.type = \"module\";\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n    for (const name in js_exports) {\n      var url = js_exports[name];\n      if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t  on_load();\n\t}\n\tcontinue;\n      }\n      var element = document.createElement('script');\n      element.onerror = on_error;\n      element.async = false;\n      element.type = \"module\";\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      element.textContent = `\n      import ${name} from \"${url}\"\n      window.${name} = ${name}\n      window._bokeh_on_load()\n      `\n      document.head.appendChild(element);\n    }\n    if (!js_urls.length && !js_modules.length) {\n      on_load()\n    }\n  };\n\n  function inject_raw_css(css) {\n    const element = document.createElement(\"style\");\n    element.appendChild(document.createTextNode(css));\n    document.body.appendChild(element);\n  }\n\n  var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.3.4.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.3.4.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.3.4.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.3.4.min.js\", \"https://cdn.holoviz.org/panel/1.3.8/dist/panel.min.js\"];\n  var js_modules = [];\n  var js_exports = {};\n  var css_urls = [];\n  var inline_js = [    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n  ];\n\n  function run_inline_js() {\n    if ((root.Bokeh !== undefined) || (force === true)) {\n      for (var i = 0; i < inline_js.length; i++) {\n\ttry {\n          inline_js[i].call(root, root.Bokeh);\n\t} catch(e) {\n\t  if (!reloading) {\n\t    throw e;\n\t  }\n\t}\n      }\n      // Cache old bokeh versions\n      if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t  Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t  Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n      }} else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    }\n    root._bokeh_is_initializing = false\n  }\n\n  function load_or_wait() {\n    // Implement a backoff loop that tries to ensure we do not load multiple\n    // versions of Bokeh and its dependencies at the same time.\n    // In recent versions we use the root._bokeh_is_initializing flag\n    // to determine whether there is an ongoing attempt to initialize\n    // bokeh, however for backward compatibility we also try to ensure\n    // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n    // before older versions are fully initialized.\n    if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n      root._bokeh_is_initializing = false;\n      root._bokeh_onload_callbacks = undefined;\n      console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n      load_or_wait();\n    } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n      setTimeout(load_or_wait, 100);\n    } else {\n      root._bokeh_is_initializing = true\n      root._bokeh_onload_callbacks = []\n      var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n      if (!reloading && !bokeh_loaded) {\n\troot.Bokeh = undefined;\n      }\n      load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n      });\n    }\n  }\n  // Give older versions of the autoload script a head-start to ensure\n  // they initialize before we start loading newer version.\n  setTimeout(load_or_wait, 100)\n}(window));",
      "application/vnd.holoviews_load.v0+json": ""
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n  window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n    function JupyterCommManager() {\n    }\n\n    JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n      if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        comm_manager.register_target(comm_id, function(comm) {\n          comm.on_msg(msg_handler);\n        });\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n          comm.onMsg = msg_handler;\n        });\n      } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n        google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n          var messages = comm.messages[Symbol.asyncIterator]();\n          function processIteratorResult(result) {\n            var message = result.value;\n            console.log(message)\n            var content = {data: message.data, comm_id};\n            var buffers = []\n            for (var buffer of message.buffers || []) {\n              buffers.push(new DataView(buffer))\n            }\n            var metadata = message.metadata || {};\n            var msg = {content, buffers, metadata}\n            msg_handler(msg);\n            return messages.next().then(processIteratorResult);\n          }\n          return messages.next().then(processIteratorResult);\n        })\n      }\n    }\n\n    JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n      if (comm_id in window.PyViz.comms) {\n        return window.PyViz.comms[comm_id];\n      } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n        if (msg_handler) {\n          comm.on_msg(msg_handler);\n        }\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n        comm.open();\n        if (msg_handler) {\n          comm.onMsg = msg_handler;\n        }\n      } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n        var comm_promise = google.colab.kernel.comms.open(comm_id)\n        comm_promise.then((comm) => {\n          window.PyViz.comms[comm_id] = comm;\n          if (msg_handler) {\n            var messages = comm.messages[Symbol.asyncIterator]();\n            function processIteratorResult(result) {\n              var message = result.value;\n              var content = {data: message.data};\n              var metadata = message.metadata || {comm_id};\n              var msg = {content, metadata}\n              msg_handler(msg);\n              return messages.next().then(processIteratorResult);\n            }\n            return messages.next().then(processIteratorResult);\n          }\n        }) \n        var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n          return comm_promise.then((comm) => {\n            comm.send(data, metadata, buffers, disposeOnDone);\n          });\n        };\n        var comm = {\n          send: sendClosure\n        };\n      }\n      window.PyViz.comms[comm_id] = comm;\n      return comm;\n    }\n    window.PyViz.comm_manager = new JupyterCommManager();\n    \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n  var div = document.createElement(\"div\");\n  var script = document.createElement(\"script\");\n  node.appendChild(div);\n  node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n  var output_area = handle.output_area;\n  var output = handle.output;\n  if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n    return\n  }\n  var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n  var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n  if (id !== undefined) {\n    var nchildren = toinsert.length;\n    var html_node = toinsert[nchildren-1].children[0];\n    html_node.innerHTML = output.data[HTML_MIME_TYPE];\n    var scripts = [];\n    var nodelist = html_node.querySelectorAll(\"script\");\n    for (var i in nodelist) {\n      if (nodelist.hasOwnProperty(i)) {\n        scripts.push(nodelist[i])\n      }\n    }\n\n    scripts.forEach( function (oldScript) {\n      var newScript = document.createElement(\"script\");\n      var attrs = [];\n      var nodemap = oldScript.attributes;\n      for (var j in nodemap) {\n        if (nodemap.hasOwnProperty(j)) {\n          attrs.push(nodemap[j])\n        }\n      }\n      attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n      newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n      oldScript.parentNode.replaceChild(newScript, oldScript);\n    });\n    if (JS_MIME_TYPE in output.data) {\n      toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n    }\n    output_area._hv_plot_id = id;\n    if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n      window.PyViz.plot_index[id] = Bokeh.index[id];\n    } else {\n      window.PyViz.plot_index[id] = null;\n    }\n  } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n    var bk_div = document.createElement(\"div\");\n    bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n    var script_attrs = bk_div.children[0].attributes;\n    for (var i = 0; i < script_attrs.length; i++) {\n      toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n    }\n    // store reference to server id on output_area\n    output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n  }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n  var id = handle.cell.output_area._hv_plot_id;\n  var server_id = handle.cell.output_area._bokeh_server_id;\n  if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n  var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n  if (server_id !== null) {\n    comm.send({event_type: 'server_delete', 'id': server_id});\n    return;\n  } else if (comm !== null) {\n    comm.send({event_type: 'delete', 'id': id});\n  }\n  delete PyViz.plot_index[id];\n  if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n    var doc = window.Bokeh.index[id].model.document\n    doc.clear();\n    const i = window.Bokeh.documents.indexOf(doc);\n    if (i > -1) {\n      window.Bokeh.documents.splice(i, 1);\n    }\n  }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n  delete PyViz.comms[\"hv-extension-comm\"];\n  window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n  handle_clear_output(event, {cell: {output_area: handle.output_area}})\n  handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n  function append_mime(data, metadata, element) {\n    // create a DOM node to render to\n    var toinsert = this.create_output_subarea(\n    metadata,\n    CLASS_NAME,\n    EXEC_MIME_TYPE\n    );\n    this.keyboard_manager.register_events(toinsert);\n    // Render to node\n    var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n    render(props, toinsert[0]);\n    element.append(toinsert);\n    return toinsert\n  }\n\n  events.on('output_added.OutputArea', handle_add_output);\n  events.on('output_updated.OutputArea', handle_update_output);\n  events.on('clear_output.CodeCell', handle_clear_output);\n  events.on('delete.Cell', handle_clear_output);\n  events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n  OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n    safe: true,\n    index: 0\n  });\n}\n\nif (window.Jupyter !== undefined) {\n  try {\n    var events = require('base/js/events');\n    var OutputArea = require('notebook/js/outputarea').OutputArea;\n    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n      register_renderer(events, OutputArea);\n    }\n  } catch(err) {\n  }\n}\n",
      "application/vnd.holoviews_load.v0+json": ""
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<style>*[data-root-id],\n",
       "*[data-root-id] > * {\n",
       "  box-sizing: border-box;\n",
       "  font-family: var(--jp-ui-font-family);\n",
       "  font-size: var(--jp-ui-font-size1);\n",
       "  color: var(--vscode-editor-foreground, var(--jp-ui-font-color1));\n",
       "}\n",
       "\n",
       "/* Override VSCode background color */\n",
       ".cell-output-ipywidget-background:has(\n",
       "    > .cell-output-ipywidget-background > .lm-Widget > *[data-root-id]\n",
       "  ),\n",
       ".cell-output-ipywidget-background:has(> .lm-Widget > *[data-root-id]) {\n",
       "  background-color: transparent !important;\n",
       "}\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.holoviews_exec.v0+json": "",
      "text/html": [
       "<div id='8adb6c88-f2a0-4849-bc77-2c32deb30834'>\n",
       "  <div id=\"f7a6a433-cbf6-4def-b3ec-fba7e8b11da1\" data-root-id=\"8adb6c88-f2a0-4849-bc77-2c32deb30834\" style=\"display: contents;\"></div>\n",
       "</div>\n",
       "<script type=\"application/javascript\">(function(root) {\n",
       "  var docs_json = {\"c8a49e97-847b-46f3-a3ee-b6791586b314\":{\"version\":\"3.3.4\",\"title\":\"Bokeh Application\",\"roots\":[{\"type\":\"object\",\"name\":\"panel.models.browser.BrowserInfo\",\"id\":\"8adb6c88-f2a0-4849-bc77-2c32deb30834\"},{\"type\":\"object\",\"name\":\"panel.models.comm_manager.CommManager\",\"id\":\"24c4c30a-e62e-48b2-b57b-46d50daf387a\",\"attributes\":{\"plot_id\":\"8adb6c88-f2a0-4849-bc77-2c32deb30834\",\"comm_id\":\"570a03687dbe4789bd1cf040d59b8ef4\",\"client_comm_id\":\"a038ea110fbb4602830ece0733b0d12f\"}}],\"defs\":[{\"type\":\"model\",\"name\":\"ReactiveHTML1\"},{\"type\":\"model\",\"name\":\"FlexBox1\",\"properties\":[{\"name\":\"align_content\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"align_items\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"flex_direction\",\"kind\":\"Any\",\"default\":\"row\"},{\"name\":\"flex_wrap\",\"kind\":\"Any\",\"default\":\"wrap\"},{\"name\":\"justify_content\",\"kind\":\"Any\",\"default\":\"flex-start\"}]},{\"type\":\"model\",\"name\":\"FloatPanel1\",\"properties\":[{\"name\":\"config\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"contained\",\"kind\":\"Any\",\"default\":true},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"right-top\"},{\"name\":\"offsetx\",\"kind\":\"Any\",\"default\":null},{\"name\":\"offsety\",\"kind\":\"Any\",\"default\":null},{\"name\":\"theme\",\"kind\":\"Any\",\"default\":\"primary\"},{\"name\":\"status\",\"kind\":\"Any\",\"default\":\"normalized\"}]},{\"type\":\"model\",\"name\":\"GridStack1\",\"properties\":[{\"name\":\"mode\",\"kind\":\"Any\",\"default\":\"warn\"},{\"name\":\"ncols\",\"kind\":\"Any\",\"default\":null},{\"name\":\"nrows\",\"kind\":\"Any\",\"default\":null},{\"name\":\"allow_resize\",\"kind\":\"Any\",\"default\":true},{\"name\":\"allow_drag\",\"kind\":\"Any\",\"default\":true},{\"name\":\"state\",\"kind\":\"Any\",\"default\":[]}]},{\"type\":\"model\",\"name\":\"drag1\",\"properties\":[{\"name\":\"slider_width\",\"kind\":\"Any\",\"default\":5},{\"name\":\"slider_color\",\"kind\":\"Any\",\"default\":\"black\"},{\"name\":\"value\",\"kind\":\"Any\",\"default\":50}]},{\"type\":\"model\",\"name\":\"click1\",\"properties\":[{\"name\":\"terminal_output\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"debug_name\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"clears\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"copy_to_clipboard1\",\"properties\":[{\"name\":\"fill\",\"kind\":\"Any\",\"default\":\"none\"},{\"name\":\"value\",\"kind\":\"Any\",\"default\":null}]},{\"type\":\"model\",\"name\":\"FastWrapper1\",\"properties\":[{\"name\":\"object\",\"kind\":\"Any\",\"default\":null},{\"name\":\"style\",\"kind\":\"Any\",\"default\":null}]},{\"type\":\"model\",\"name\":\"NotificationAreaBase1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"NotificationArea1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"notifications\",\"kind\":\"Any\",\"default\":[]},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0},{\"name\":\"types\",\"kind\":\"Any\",\"default\":[{\"type\":\"map\",\"entries\":[[\"type\",\"warning\"],[\"background\",\"#ffc107\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-exclamation-triangle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]},{\"type\":\"map\",\"entries\":[[\"type\",\"info\"],[\"background\",\"#007bff\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-info-circle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]}]}]},{\"type\":\"model\",\"name\":\"Notification\",\"properties\":[{\"name\":\"background\",\"kind\":\"Any\",\"default\":null},{\"name\":\"duration\",\"kind\":\"Any\",\"default\":3000},{\"name\":\"icon\",\"kind\":\"Any\",\"default\":null},{\"name\":\"message\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"notification_type\",\"kind\":\"Any\",\"default\":null},{\"name\":\"_destroyed\",\"kind\":\"Any\",\"default\":false}]},{\"type\":\"model\",\"name\":\"TemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"BootstrapTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"MaterialTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]}]}};\n",
       "  var render_items = [{\"docid\":\"c8a49e97-847b-46f3-a3ee-b6791586b314\",\"roots\":{\"8adb6c88-f2a0-4849-bc77-2c32deb30834\":\"f7a6a433-cbf6-4def-b3ec-fba7e8b11da1\"},\"root_ids\":[\"8adb6c88-f2a0-4849-bc77-2c32deb30834\"]}];\n",
       "  var docs = Object.values(docs_json)\n",
       "  if (!docs) {\n",
       "    return\n",
       "  }\n",
       "  const py_version = docs[0].version.replace('rc', '-rc.').replace('.dev', '-dev.')\n",
       "  function embed_document(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "    for (const render_item of render_items) {\n",
       "      for (const root_id of render_item.root_ids) {\n",
       "\tconst id_el = document.getElementById(root_id)\n",
       "\tif (id_el.children.length && (id_el.children[0].className === 'bk-root')) {\n",
       "\t  const root_el = id_el.children[0]\n",
       "\t  root_el.id = root_el.id + '-rendered'\n",
       "\t}\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "  function get_bokeh(root) {\n",
       "    if (root.Bokeh === undefined) {\n",
       "      return null\n",
       "    } else if (root.Bokeh.version !== py_version) {\n",
       "      if (root.Bokeh.versions === undefined || !root.Bokeh.versions.has(py_version)) {\n",
       "\treturn null\n",
       "      }\n",
       "      return root.Bokeh.versions.get(py_version);\n",
       "    } else if (root.Bokeh.version === py_version) {\n",
       "      return root.Bokeh\n",
       "    }\n",
       "    return null\n",
       "  }\n",
       "  function is_loaded(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    return (Bokeh != null && Bokeh.Panel !== undefined)\n",
       "  }\n",
       "  if (is_loaded(root)) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (is_loaded(root)) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else if (document.readyState == \"complete\") {\n",
       "        attempts++;\n",
       "        if (attempts > 200) {\n",
       "          clearInterval(timer);\n",
       "\t  var Bokeh = get_bokeh(root)\n",
       "\t  if (Bokeh == null || Bokeh.Panel == null) {\n",
       "            console.warn(\"Panel: ERROR: Unable to run Panel code because Bokeh or Panel library is missing\");\n",
       "\t  } else {\n",
       "\t    console.warn(\"Panel: WARNING: Attempting to render but not all required libraries could be resolved.\")\n",
       "\t    embed_document(root)\n",
       "\t  }\n",
       "        }\n",
       "      }\n",
       "    }, 25, root)\n",
       "  }\n",
       "})(window);</script>"
      ]
     },
     "metadata": {
      "application/vnd.holoviews_exec.v0+json": {
       "id": "8adb6c88-f2a0-4849-bc77-2c32deb30834"
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import panel as pn  # GUI\n",
    "pn.extension()\n",
    "import panel as pn\n",
    "import param\n",
    "\n",
    "# 定义 cbfs 类\n",
    "class cbfs(param.Parameterized):\n",
    "    \n",
    "    # 初始化函数\n",
    "    def __init__(self, tools, **params):\n",
    "        super(cbfs, self).__init__(**params)\n",
    "        self.panels = []  # 存储 GUI 面板\n",
    "        self.functions = [format_tool_to_openai_function(f) for f in tools]  # 将tools格式化为 OpenAI 函数\n",
    "        self.model = ChatOpenAI(temperature=0).bind(functions=self.functions)  # 创建 ChatOpenAI 模型\n",
    "        self.memory = ConversationBufferMemory(return_messages=True, memory_key=\"chat_history\")  # 创建 ConversationBufferMemory\n",
    "        self.prompt = ChatPromptTemplate.from_messages([\n",
    "            (\"system\", \"You are helpful but sassy assistant\"),\n",
    "            MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "            (\"user\", \"{input}\"),\n",
    "            MessagesPlaceholder(variable_name=\"agent_scratchpad\")\n",
    "        ])  # 创建 ChatPromptTemplate\n",
    "        self.chain = RunnablePassthrough.assign(\n",
    "            agent_scratchpad=lambda x: format_to_openai_functions(x[\"intermediate_steps\"])\n",
    "        ) | self.prompt | self.model | OpenAIFunctionsAgentOutputParser()  # 创建处理链\n",
    "        self.qa = AgentExecutor(agent=self.chain, tools=tools, verbose=False, memory=self.memory)  # 创建 AgentExecutor\n",
    "    \n",
    "    # 对话链函数\n",
    "    def convchain(self, query):\n",
    "        if not query:\n",
    "            return\n",
    "        inp.value = ''\n",
    "        result = self.qa.invoke({\"input\": query})\n",
    "        self.answer = result['output']\n",
    "        self.panels.extend([\n",
    "            pn.Row('User:', pn.pane.Markdown(query, width=450)),\n",
    "            pn.Row('ChatBot:', pn.pane.Markdown(self.answer, width=450, styles={'background-color': '#F6F6F6'}))\n",
    "        ])\n",
    "        return pn.WidgetBox(*self.panels, scroll=True)\n",
    "\n",
    "    # 清除历史记录函数\n",
    "    def clr_history(self, count=0):\n",
    "        self.chat_history = []\n",
    "        return\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "080c3827-87fc-4dbe-a566-ca95b3f9068e",
   "metadata": {},
   "outputs": [
    {
     "data": {},
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.holoviews_exec.v0+json": "",
      "text/html": [
       "<div id='255ca157-fe34-446c-a413-5d513fdd9acf'>\n",
       "  <div id=\"dc2d496c-d5f7-4211-b406-3e97521f0e3f\" data-root-id=\"255ca157-fe34-446c-a413-5d513fdd9acf\" style=\"display: contents;\"></div>\n",
       "</div>\n",
       "<script type=\"application/javascript\">(function(root) {\n",
       "  var docs_json = {\"076bcfdd-be8a-4a4b-ac5a-c9946e6a9b97\":{\"version\":\"3.3.4\",\"title\":\"Bokeh Application\",\"roots\":[{\"type\":\"object\",\"name\":\"panel.models.layout.Column\",\"id\":\"255ca157-fe34-446c-a413-5d513fdd9acf\",\"attributes\":{\"name\":\"Column00254\",\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/css/loading.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"1b7599cb-1d74-4ced-8664-1b56d4a5538f\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/css/listpanel.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/bundled/theme/default.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/bundled/theme/native.css\"}}],\"margin\":0,\"align\":\"start\",\"children\":[{\"type\":\"object\",\"name\":\"Row\",\"id\":\"46d4e33c-bd68-42aa-a0e4-cf6a0045321c\",\"attributes\":{\"name\":\"Row00252\",\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"id\":\"1b7599cb-1d74-4ced-8664-1b56d4a5538f\"},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"margin\":0,\"align\":\"start\",\"children\":[{\"type\":\"object\",\"name\":\"panel.models.markup.HTML\",\"id\":\"35f82e85-6424-421c-945c-4a6d9b6d4b82\",\"attributes\":{\"css_classes\":[\"markdown\"],\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"24a30439-6929-4010-95da-4f015350dc66\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/css/markdown.css\"}},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"margin\":[5,10],\"align\":\"start\",\"text\":\"&lt;h1 id=&quot;\\u5bf9\\u8bdd\\u673a\\u5668\\u4eba&quot;&gt;\\u5bf9\\u8bdd\\u673a\\u5668\\u4eba &lt;a class=&quot;header-anchor&quot; href=&quot;#\\u5bf9\\u8bdd\\u673a\\u5668\\u4eba&quot;&gt;\\u00b6&lt;/a&gt;&lt;/h1&gt;\\n\"}}]}},{\"type\":\"object\",\"name\":\"panel.models.tabs.Tabs\",\"id\":\"4d52253e-3e54-4223-b8ae-eee5e0abb35b\",\"attributes\":{\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"margin\":0,\"align\":\"start\",\"tabs\":[{\"type\":\"object\",\"name\":\"TabPanel\",\"id\":\"2bd3d6f1-9598-480d-9e95-da2ea24b781b\",\"attributes\":{\"name\":\"Column00249\",\"title\":\"\\u53d1\\u9001\",\"child\":{\"type\":\"object\",\"name\":\"panel.models.layout.Column\",\"id\":\"342bcff2-6671-4a01-9027-732b89fbab35\",\"attributes\":{\"name\":\"Column00249\",\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"id\":\"1b7599cb-1d74-4ced-8664-1b56d4a5538f\"},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"margin\":0,\"align\":\"start\",\"children\":[{\"type\":\"object\",\"name\":\"Row\",\"id\":\"5be077a0-b4f2-4fb0-b3ca-47f1282b8988\",\"attributes\":{\"name\":\"Row00240\",\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"id\":\"1b7599cb-1d74-4ced-8664-1b56d4a5538f\"},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"margin\":0,\"align\":\"start\",\"children\":[{\"type\":\"object\",\"name\":\"TextInput\",\"id\":\"d8ebd6d6-69da-451e-a9a4-2fbe0e521ef8\",\"attributes\":{\"js_property_callbacks\":{\"type\":\"map\",\"entries\":[[\"change:value\",[{\"type\":\"object\",\"name\":\"CustomJS\",\"id\":\"92f05030-6f25-4e7e-9c71-da511b2c7a3f\",\"attributes\":{\"tags\":[[2595565387680,[null,\"value\"],[null,\"loading\"]]],\"args\":{\"type\":\"map\",\"entries\":[[\"bidirectional\",false],[\"properties\",{\"type\":\"map\",\"entries\":[[\"value\",\"loading\"]]}],[\"source\",{\"id\":\"d8ebd6d6-69da-451e-a9a4-2fbe0e521ef8\"}],[\"target\",{\"type\":\"object\",\"name\":\"panel.models.layout.Column\",\"id\":\"a67cfdf8-35a9-4909-a152-34f05b0b6594\",\"attributes\":{\"name\":\"Column00246\",\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"id\":\"1b7599cb-1d74-4ced-8664-1b56d4a5538f\"},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"height\":400,\"min_height\":400,\"margin\":0,\"align\":\"start\",\"children\":[{\"type\":\"object\",\"name\":\"panel.models.markup.HTML\",\"id\":\"2c8c3e7f-8ad1-4bc4-ba9e-37a9f97f925e\",\"attributes\":{\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"margin\":[5,10],\"align\":\"start\",\"text\":\"&lt;pre&gt; &lt;/pre&gt;\"}}]}}]]},\"code\":\"\\n    if ('value'.startsWith('event:')) {\\n      var value = true\\n    } else {\\n      var value = source['value'];\\n      value = value;\\n    }\\n    if (typeof value !== 'boolean' || source.labels !== ['Loading']) {\\n      value = true\\n    }\\n    var css_classes = target.css_classes.slice()\\n    var loading_css = ['pn-loading', 'pn-arc']\\n    if (value) {\\n      for (var css of loading_css) {\\n        if (!(css in css_classes)) {\\n          css_classes.push(css)\\n        }\\n      }\\n    } else {\\n     for (var css of loading_css) {\\n        var index = css_classes.indexOf(css)\\n        if (index > -1) {\\n          css_classes.splice(index, 1)\\n        }\\n      }\\n    }\\n    target['css_classes'] = css_classes\\n    \"}}]]]},\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"595a7f80-da26-4ffe-8c6e-e6c816547589\"},{\"id\":\"5c04feda-146f-4522-b933-67ac59664a60\"},{\"id\":\"fc2a3043-a298-481f-a79f-0262a40b67bf\"}],\"width\":300,\"min_width\":300,\"margin\":[5,10],\"align\":\"start\",\"placeholder\":\"\\u8bf7\\u8f93\\u5165\\u6587\\u672c...\",\"max_length\":5000}}]}},{\"type\":\"object\",\"name\":\"Div\",\"id\":\"a16f3502-1888-449b-adee-94b62a921134\",\"attributes\":{\"name\":\"Divider00241\",\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"2b611a48-5718-47af-af2a-cdeada849b96\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/css/loading.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"6a49766f-54c4-471f-b2bc-077b988736b5\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/css/divider.css\"}}],\"margin\":0,\"width_policy\":\"fit\",\"align\":\"start\",\"text\":\"<hr>\"}},{\"id\":\"a67cfdf8-35a9-4909-a152-34f05b0b6594\"},{\"type\":\"object\",\"name\":\"Div\",\"id\":\"585ea247-43eb-422b-afab-ec2aab199088\",\"attributes\":{\"name\":\"Divider00248\",\"stylesheets\":[\"\\n:host(.pn-loading.pn-arc):before, .pn-loading.pn-arc:before {\\n  background-image: url(\\\"\\\");\\n  background-size: auto calc(min(50%, 400px));\\n}\",{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"6c097048-240e-476a-93ff-974273fc1cf6\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/css/loading.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"067a90a1-d379-482c-9da3-7abaaeb762d8\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.3.8/dist/css/divider.css\"}}],\"margin\":0,\"width_policy\":\"fit\",\"align\":\"start\",\"text\":\"<hr>\"}}]}}}}]}}]}},{\"type\":\"object\",\"name\":\"panel.models.comm_manager.CommManager\",\"id\":\"4f9c8f10-a5e5-42b4-a89f-70ebec20dd38\",\"attributes\":{\"plot_id\":\"255ca157-fe34-446c-a413-5d513fdd9acf\",\"comm_id\":\"8bd24abf38f748efb3d1b9a0913f8e12\",\"client_comm_id\":\"e77a6908631b490dae9b124a35bf9a02\"}}],\"defs\":[{\"type\":\"model\",\"name\":\"ReactiveHTML1\"},{\"type\":\"model\",\"name\":\"FlexBox1\",\"properties\":[{\"name\":\"align_content\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"align_items\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"flex_direction\",\"kind\":\"Any\",\"default\":\"row\"},{\"name\":\"flex_wrap\",\"kind\":\"Any\",\"default\":\"wrap\"},{\"name\":\"justify_content\",\"kind\":\"Any\",\"default\":\"flex-start\"}]},{\"type\":\"model\",\"name\":\"FloatPanel1\",\"properties\":[{\"name\":\"config\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"contained\",\"kind\":\"Any\",\"default\":true},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"right-top\"},{\"name\":\"offsetx\",\"kind\":\"Any\",\"default\":null},{\"name\":\"offsety\",\"kind\":\"Any\",\"default\":null},{\"name\":\"theme\",\"kind\":\"Any\",\"default\":\"primary\"},{\"name\":\"status\",\"kind\":\"Any\",\"default\":\"normalized\"}]},{\"type\":\"model\",\"name\":\"GridStack1\",\"properties\":[{\"name\":\"mode\",\"kind\":\"Any\",\"default\":\"warn\"},{\"name\":\"ncols\",\"kind\":\"Any\",\"default\":null},{\"name\":\"nrows\",\"kind\":\"Any\",\"default\":null},{\"name\":\"allow_resize\",\"kind\":\"Any\",\"default\":true},{\"name\":\"allow_drag\",\"kind\":\"Any\",\"default\":true},{\"name\":\"state\",\"kind\":\"Any\",\"default\":[]}]},{\"type\":\"model\",\"name\":\"drag1\",\"properties\":[{\"name\":\"slider_width\",\"kind\":\"Any\",\"default\":5},{\"name\":\"slider_color\",\"kind\":\"Any\",\"default\":\"black\"},{\"name\":\"value\",\"kind\":\"Any\",\"default\":50}]},{\"type\":\"model\",\"name\":\"click1\",\"properties\":[{\"name\":\"terminal_output\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"debug_name\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"clears\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"copy_to_clipboard1\",\"properties\":[{\"name\":\"fill\",\"kind\":\"Any\",\"default\":\"none\"},{\"name\":\"value\",\"kind\":\"Any\",\"default\":null}]},{\"type\":\"model\",\"name\":\"FastWrapper1\",\"properties\":[{\"name\":\"object\",\"kind\":\"Any\",\"default\":null},{\"name\":\"style\",\"kind\":\"Any\",\"default\":null}]},{\"type\":\"model\",\"name\":\"NotificationAreaBase1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"NotificationArea1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"notifications\",\"kind\":\"Any\",\"default\":[]},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0},{\"name\":\"types\",\"kind\":\"Any\",\"default\":[{\"type\":\"map\",\"entries\":[[\"type\",\"warning\"],[\"background\",\"#ffc107\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-exclamation-triangle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]},{\"type\":\"map\",\"entries\":[[\"type\",\"info\"],[\"background\",\"#007bff\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-info-circle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]}]}]},{\"type\":\"model\",\"name\":\"Notification\",\"properties\":[{\"name\":\"background\",\"kind\":\"Any\",\"default\":null},{\"name\":\"duration\",\"kind\":\"Any\",\"default\":3000},{\"name\":\"icon\",\"kind\":\"Any\",\"default\":null},{\"name\":\"message\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"notification_type\",\"kind\":\"Any\",\"default\":null},{\"name\":\"_destroyed\",\"kind\":\"Any\",\"default\":false}]},{\"type\":\"model\",\"name\":\"TemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"BootstrapTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"MaterialTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]}]}};\n",
       "  var render_items = [{\"docid\":\"076bcfdd-be8a-4a4b-ac5a-c9946e6a9b97\",\"roots\":{\"255ca157-fe34-446c-a413-5d513fdd9acf\":\"dc2d496c-d5f7-4211-b406-3e97521f0e3f\"},\"root_ids\":[\"255ca157-fe34-446c-a413-5d513fdd9acf\"]}];\n",
       "  var docs = Object.values(docs_json)\n",
       "  if (!docs) {\n",
       "    return\n",
       "  }\n",
       "  const py_version = docs[0].version.replace('rc', '-rc.').replace('.dev', '-dev.')\n",
       "  function embed_document(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "    for (const render_item of render_items) {\n",
       "      for (const root_id of render_item.root_ids) {\n",
       "\tconst id_el = document.getElementById(root_id)\n",
       "\tif (id_el.children.length && (id_el.children[0].className === 'bk-root')) {\n",
       "\t  const root_el = id_el.children[0]\n",
       "\t  root_el.id = root_el.id + '-rendered'\n",
       "\t}\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "  function get_bokeh(root) {\n",
       "    if (root.Bokeh === undefined) {\n",
       "      return null\n",
       "    } else if (root.Bokeh.version !== py_version) {\n",
       "      if (root.Bokeh.versions === undefined || !root.Bokeh.versions.has(py_version)) {\n",
       "\treturn null\n",
       "      }\n",
       "      return root.Bokeh.versions.get(py_version);\n",
       "    } else if (root.Bokeh.version === py_version) {\n",
       "      return root.Bokeh\n",
       "    }\n",
       "    return null\n",
       "  }\n",
       "  function is_loaded(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    return (Bokeh != null && Bokeh.Panel !== undefined)\n",
       "  }\n",
       "  if (is_loaded(root)) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (is_loaded(root)) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else if (document.readyState == \"complete\") {\n",
       "        attempts++;\n",
       "        if (attempts > 200) {\n",
       "          clearInterval(timer);\n",
       "\t  var Bokeh = get_bokeh(root)\n",
       "\t  if (Bokeh == null || Bokeh.Panel == null) {\n",
       "            console.warn(\"Panel: ERROR: Unable to run Panel code because Bokeh or Panel library is missing\");\n",
       "\t  } else {\n",
       "\t    console.warn(\"Panel: WARNING: Attempting to render but not all required libraries could be resolved.\")\n",
       "\t    embed_document(root)\n",
       "\t  }\n",
       "        }\n",
       "      }\n",
       "    }, 25, root)\n",
       "  }\n",
       "})(window);</script>"
      ],
      "text/plain": [
       "Column\n",
       "    [0] Row\n",
       "        [0] Markdown(str)\n",
       "    [1] Tabs\n",
       "        [0] Column\n",
       "            [0] Row\n",
       "                [0] TextInput(placeholder='请输入文本...')\n",
       "            [1] Divider()\n",
       "            [2] ParamFunction(function, _pane=Str, defer_load=False, height=400, loading_indicator=True)\n",
       "            [3] Divider()"
      ]
     },
     "execution_count": 67,
     "metadata": {
      "application/vnd.holoviews_exec.v0+json": {
       "id": "255ca157-fe34-446c-a413-5d513fdd9acf"
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 创建 cbfs 对象，传递工具列表\n",
    "cb = cbfs(tools)\n",
    "\n",
    "# 创建文本输入框组件\n",
    "inp = pn.widgets.TextInput(placeholder='请输入文本...')\n",
    "\n",
    "# 将 convchain 方法与输入框进行绑定，形成对话链\n",
    "conversation = pn.bind(cb.convchain, inp) \n",
    "\n",
    "# 创建第一个选项卡组件\n",
    "tab1 = pn.Column(\n",
    "    pn.Row(inp),  # 显示文本输入框\n",
    "    pn.layout.Divider(),\n",
    "    pn.panel(conversation, loading_indicator=True, height=400),  # 显示对话链面板\n",
    "    pn.layout.Divider(),\n",
    ")\n",
    "\n",
    "# 创建仪表板，包含标题和选项卡\n",
    "dashboard = pn.Column(\n",
    "    pn.Row(pn.pane.Markdown('# 对话机器人')),  # 显示标题\n",
    "    pn.Tabs(('发送', tab1))  # 创建选项卡\n",
    ")\n",
    "\n",
    "# 显示仪表板\n",
    "dashboard\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "52ab4b61",
   "metadata": {},
   "source": [
    "聊天机器人效果展示如下：\n",
    "\n",
    "![](../../figures/chat_bot.png)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "41804e2f-4dc1-4763-8893-5e76ab5d8f14",
   "metadata": {},
   "source": [
    "# 四、英文版提示\n",
    "\n",
    "总结一下构建对话机器人的流程：\n",
    "\n",
    "定义tools和functions --> 构建模型 --> 加入中间步骤结果 --> 加入记忆机制 --> 交互界面  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "6298c92f",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"You are helpful but sassy assistant\"),\n",
    "    MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "    (\"user\", \"{input}\"),\n",
    "    MessagesPlaceholder(variable_name=\"agent_scratchpad\")\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "4d305357",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 构造agent的处理链\n",
    "agent_chain = RunnablePassthrough.assign(\n",
    "    agent_scratchpad= lambda x: format_to_openai_functions(x[\"intermediate_steps\"])\n",
    ") | prompt | model | OpenAIFunctionsAgentOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "fee7e9f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 加入对话记忆模块\n",
    "from langchain.memory import ConversationBufferMemory\n",
    "memory = ConversationBufferMemory(return_messages=True,memory_key=\"chat_history\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "952356fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 封装AgentExecutor\n",
    "agent_executor = AgentExecutor(agent=agent_chain, tools=tools, verbose=True, memory=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "41b541a4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mNice to meet you, Bob! How can I assist you today?\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'my name is bob',\n",
       " 'chat_history': [HumanMessage(content='my name is bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?')],\n",
       " 'output': 'Nice to meet you, Bob! How can I assist you today?'}"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"my name is bob\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "31c73aec",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mYour name is Bob. How can I assist you today, Bob?\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'what is my name',\n",
       " 'chat_history': [HumanMessage(content='my name is bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?'),\n",
       "  HumanMessage(content='what is my name'),\n",
       "  AIMessage(content='Your name is Bob. How can I assist you today, Bob?')],\n",
       " 'output': 'Your name is Bob. How can I assist you today, Bob?'}"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"what is my name\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "022675d2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `get_current_temperature` with `{'latitude': 37.7749, 'longitude': -122.4194}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3m现在温度是 9.5°C\u001b[0m\u001b[32;1m\u001b[1;3mThe current temperature in San Francisco is 9.5°C.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'whats the weather in sf?',\n",
       " 'chat_history': [HumanMessage(content='my name is bob'),\n",
       "  AIMessage(content='Nice to meet you, Bob! How can I assist you today?'),\n",
       "  HumanMessage(content='what is my name'),\n",
       "  AIMessage(content='Your name is Bob. How can I assist you today, Bob?'),\n",
       "  HumanMessage(content='whats the weather in sf?'),\n",
       "  AIMessage(content='The current temperature in San Francisco is 9.5°C.')],\n",
       " 'output': 'The current temperature in San Francisco is 9.5°C.'}"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"whats the weather in sf?\"})"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
